package cn.lightsky.infiniteindicator; import android.content.Context; import android.content.res.TypedArray; import android.os.Handler; import android.os.Message; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.animation.Interpolator; import android.widget.RelativeLayout; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.List; import cn.lightsky.infiniteindicator.indicator.PageIndicator; import cn.lightsky.infiniteindicator.recycle.RecyclingPagerAdapter; import cn.lightsky.infiniteindicator.recycle.RecyleAdapter; /** * Created by lightSky on 2014/12/22. */ public class InfiniteIndicator extends RelativeLayout implements RecyclingPagerAdapter.DataChangeListener, ViewPager.OnPageChangeListener { private Context mContext; private ViewPager mViewPager; private PageIndicator mIndicator; private RecyleAdapter mRecyleAdapter; private DurationScroller scroller; private final ScrollHandler handler; private boolean isScrolling; private boolean isStopByTouch; private float downX = 0f; private float touchX = 0f; public static final int MSG_SCROLL = 1000; public static final int PAGE_COUNT_FACTOR = 100; private IndicatorConfiguration configuration; public InfiniteIndicator(Context context) { this(context, null); } public InfiniteIndicator(Context context, AttributeSet attrs) { this(context, attrs, 0); } public InfiniteIndicator(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.InfiniteIndicator, 0, 0); int indicatorType = attributes.getInt(R.styleable.InfiniteIndicator_indicator_type, 0); if (indicatorType == 0) { LayoutInflater.from(context).inflate(R.layout.layout_default_indicator, this, true); } else if (indicatorType == 1) { LayoutInflater.from(context).inflate(R.layout.layout_anim_circle_indicator, this, true); } else { LayoutInflater.from(context).inflate(R.layout.layout_anim_line_indicator, this, true); } attributes.recycle(); mViewPager = (ViewPager) findViewById(R.id.view_pager); handler = new ScrollHandler(this); } public void init(IndicatorConfiguration configuration) { this.configuration = configuration; mRecyleAdapter = new RecyleAdapter(mContext ,configuration.getViewBinder() ,configuration.getOnPageClickListener()); mRecyleAdapter.setDataChangeListener(this); mViewPager.setAdapter(mRecyleAdapter); mViewPager.addOnPageChangeListener(this); mRecyleAdapter.setIsLoop(configuration.isLoop()); mRecyleAdapter.setImageLoader(configuration.getImageLoader()); setScroller(); initIndicator(); } public void notifyDataChange(List<Page> pages) { if (pages != null && !pages.isEmpty()) { mRecyleAdapter.setPages(pages); } scrollToIndex(0); if (configuration.isAutoScroll()) { start(); } } public void initIndicator() { if (configuration.isDrawIndicator()) { mIndicator = (PageIndicator) findViewById(configuration.getPageIndicator().getResourceId()); mIndicator.setViewPager(mViewPager); } } /** * return PageIndicator for develop to custome indicator * * @return */ public PageIndicator getPagerIndicator() { return mIndicator; } /** * scroll to given index page by loop and offset * page to display */ private void scrollToIndex(int offset) { if (configuration.isLoop() && getRealCount() > 1) { mViewPager.setCurrentItem(getIndex(offset)); } else { mViewPager.setCurrentItem(offset); } if (mIndicator != null) { mIndicator.setCurrentItem(offset); } } /** * get the item index of viewpager by given offset * * @param offset the real index of pages * @return */ private int getIndex(int offset) { return getRealCount() * PAGE_COUNT_FACTOR / 2 - (getRealCount() * PAGE_COUNT_FACTOR / 2 % getRealCount ()) + offset; } /** * start auto scroll * * @param delayTimeInMills first scroll delay time,default is 2500ms */ public void start() { start(configuration.getInterval()); } public void start(long delayTimeInMills) { if (configuration == null) { throw new RuntimeException("You should init a configuration first"); } if (getRealCount() > 1 && isScrolling == false && configuration.isLoop()) { isScrolling = true; sendScrollMessage(delayTimeInMills); } } public void stop() { isScrolling = false; handler.removeMessages(MSG_SCROLL); } private void sendScrollMessage() { sendScrollMessage(configuration.getInterval()); } /** * remove messages before, keeps one message is running at most **/ private void sendScrollMessage(long delayTimeInMills) { handler.removeMessages(MSG_SCROLL); handler.sendEmptyMessageDelayed(MSG_SCROLL, delayTimeInMills); } /** * modify duration of ViewPager */ private void setScroller() { try { Field scrollerField = ViewPager.class.getDeclaredField("mScroller"); scrollerField.setAccessible(true); Field interpolatorField = ViewPager.class.getDeclaredField("sInterpolator"); interpolatorField.setAccessible(true); scroller = new DurationScroller(getContext(), (Interpolator) interpolatorField.get(null)); scrollerField.set(mViewPager, scroller); scroller.setScrollDurationFactor(configuration.getScrollFactor()); } catch (Exception e) { e.printStackTrace(); } } private int getRealCount() { return mRecyleAdapter.getRealCount(); } private int getRealPosition(int position) { return mRecyleAdapter.getRealPosition(position); } /** * scroll only once */ public void scrollOnce() { PagerAdapter adapter = mViewPager.getAdapter(); int currentItem = mViewPager.getCurrentItem(); int totalCount; if (adapter == null || (totalCount = adapter.getCount()) <= 1) { return; } int nextItem = (configuration.getDirection() == IndicatorConfiguration.LEFT) ? --currentItem : ++currentItem; if (nextItem < 0) { if (configuration.isLoop()) { mViewPager.setCurrentItem(totalCount - 1); } } else if (nextItem == totalCount) { if (configuration.isLoop()) { mViewPager.setCurrentItem(0); } } else { mViewPager.setCurrentItem(nextItem, true); } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (configuration == null) { return super.dispatchTouchEvent(ev); } int action = MotionEventCompat.getActionMasked(ev); if (configuration.isStopWhenTouch()) { if ((action == MotionEvent.ACTION_DOWN) && isScrolling) { isStopByTouch = true; stop(); } else if (ev.getAction() == MotionEvent.ACTION_UP && isStopByTouch) { start(); } } return super.dispatchTouchEvent(ev); } @Override public void notifyDataChange() { if (mIndicator != null) { mIndicator.notifyDataSetChanged(); } } public static class ScrollHandler extends Handler { public WeakReference<InfiniteIndicator> mWeakReference; public ScrollHandler(InfiniteIndicator infiniteIndicatorLayout) { mWeakReference = new WeakReference<InfiniteIndicator>(infiniteIndicatorLayout); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); InfiniteIndicator infiniteIndicatorLayout = mWeakReference.get(); if (infiniteIndicatorLayout != null) { switch (msg.what) { case MSG_SCROLL: infiniteIndicatorLayout.scrollOnce(); infiniteIndicatorLayout.sendScrollMessage(); default: break; } } } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (mIndicator != null) { mIndicator.onPageScrolled(getRealPosition(position), positionOffset, positionOffsetPixels); } if (configuration.getOnPageChangeListener() != null) { configuration.getOnPageChangeListener().onPageScrolled( getRealPosition(position), positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { if (mIndicator != null) { mIndicator.onPageSelected(getRealPosition(position)); } if (configuration.getOnPageChangeListener() != null) { configuration.getOnPageChangeListener().onPageSelected(getRealPosition(position)); } } @Override public void onPageScrollStateChanged(int state) { if (mIndicator != null) { mIndicator.onPageScrollStateChanged(state); } if (configuration.getOnPageChangeListener() != null) { configuration.getOnPageChangeListener().onPageScrollStateChanged(state); } } public void setCurrentItem(int index) { if (index > getRealCount() - 1) { throw new IndexOutOfBoundsException("index is " + index + "current " + "list size is " + getRealCount()); } scrollToIndex(index); } }